package com.ds.bpm.engine.database.hib;

import com.ds.common.database.C3P0ConnectionProvider;
import com.ds.common.database.ConnectionManager;
import com.ds.common.database.ConnectionManagerFactory;
import com.ds.context.JDSActionContext;
import org.hibernate.HibernateException;
import org.hibernate.StatelessSession;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

/**
 * Configures and provides access to Hibernate sessions, tied to the current thread of execution. Follows the Thread
 * Local Session pattern, see {@link http://hibernate.org/42.html }.
 */
public class HibernateSessionFactory {

    /**
     * Location of hibernate.cfg.xml file. Location should be on the classpath as Hibernate uses #resourceAsStream style
     * lookup for its configuration file. The default classpath location of the hibernate config file is in the default
     * package. Use #setConfigFile() to update the location of the configuration file for the current session.
     */

    private static final ThreadLocal<StatelessSession> threadLocal = new ThreadLocal<StatelessSession>();
    private static org.hibernate.SessionFactory sessionFactory;

    private static final Configuration   configuration = new Configuration();
    private static       ServiceRegistry serviceRegistry;

    static {
        try {
            configuration.configure();

            final ConnectionManager connectionManager = ConnectionManagerFactory.getInstance().getConnectionManager("bpm");
            final C3P0ConnectionProvider connectionProvider = (C3P0ConnectionProvider) connectionManager.getConnectionProvider();

            if (connectionProvider.getProviderConfig().getDriver().toLowerCase().indexOf("mysql") > -1) {
                configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect");
            } else 	if (connectionProvider.getProviderConfig().getDriver().toLowerCase().indexOf("oracle") > -1) {
                configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
            } else 	 {
                configuration.setProperty("hibernate.dialect", "org.hibernate.dialect.Kingbase8Dialect");
            }

//			configuration.setProperty("connection.autoReconnect","true");
//			configuration.setProperty("connection.autoReconnectForPools","true");
//			configuration.setProperty("connection.is-connection-validation-required","true");
            configuration.setProperty("hibernate.connection.url", connectionProvider.getProviderConfig().getServerURL());
            configuration.setProperty("hibernate.connection.driver_class", connectionProvider.getProviderConfig().getDriver());
            configuration.setProperty("hibernate.connection.username", connectionProvider.getProviderConfig().getUsername());
            configuration.setProperty("hibernate.connection.password", connectionProvider.getProviderConfig().getPassword());



            serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).configure().build();


            Metadata metadata = new MetadataSources(serviceRegistry).getMetadataBuilder() .applyImplicitNamingStrategy(ImplicitNamingStrategyComponentPathImpl.INSTANCE).build();
            sessionFactory = metadata.getSessionFactoryBuilder().build();

        } catch (final Exception e) {
            System.err.println("%%%% Error Creating SessionFactory %%%%");
            e.printStackTrace();
        }
    }

    private HibernateSessionFactory() {
    }

    /**
     * Returns the ThreadLocal Session instance. Lazy initialize the <code>SessionFactory</code> if needed.
     *
     * @return Session
     * @throws HibernateException
     */
    public static StatelessSession getSession() throws HibernateException {

        // StatelessSession session =(StatelessSession)
        // JDSActionContext.getActionContext().getContext().get("hibsession");
        StatelessSession session = (StatelessSession) threadLocal.get();

        if (session == null) {
            if (sessionFactory == null) {
                rebuildSessionFactory();
            }
            session = (sessionFactory != null) ? sessionFactory.openStatelessSession() : null;
            JDSActionContext.getActionContext().getContext().put("hibsession", session);

            threadLocal.set(session);

        }

        return session;
    }

    /**
     * Rebuild hibernate session factory
     *
     */
    public static void rebuildSessionFactory() {
        try {
            configuration.configure();
            serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
            Metadata metadata = new MetadataSources(serviceRegistry).getMetadataBuilder().applyImplicitNamingStrategy(ImplicitNamingStrategyJpaCompliantImpl.INSTANCE).build();

            sessionFactory = metadata.getSessionFactoryBuilder().build();

        } catch (final Exception e) {
            System.err.println("%%%% Error Creating SessionFactory %%%%");
            e.printStackTrace();
        }
    }

    /**
     * Close the single hibernate session instance.
     *
     * @throws HibernateException
     */
    public static void closeSession() throws HibernateException {
        final StatelessSession session = (StatelessSession) threadLocal.get();
        threadLocal.set(null);
        // StatelessSession session = (StatelessSession)
        // JDSActionContext.getActionContext().getContext().get("hibsession");
        JDSActionContext.getActionContext().getContext().put("hibsession", null);
        if (session != null) {
            session.close();

            // DbManager.getInstance().releaseConnection(session.connection());
        }
    }

    /**
     * return session factory
     *
     */
    public static org.hibernate.SessionFactory getSessionFactory() {


        if (sessionFactory == null) {
            rebuildSessionFactory();
        }
        return sessionFactory;
    }

    /**
     * return hibernate configuration
     *
     */
    public static Configuration getConfiguration() {
        return configuration;
    }

}